﻿// Spun AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Spun.Utility.Locking;

namespace Spun.Controllers
{
    public class ControllerContext
    {
        public ControllerContext()
        {
            BaseControllers = new Dictionary<string, IController>(StringComparer.CurrentCultureIgnoreCase);
            LockingContext = new LockingContext();
        }

        public IController this[string Name]
        {
            get
            {
                using (LockingContext.ReadLock)
                    return BaseControllers[Name];
            }
            set
            {
                using (LockingContext.WriteLock)
                    BaseControllers[Name] = value;
            }
        }

        public bool TryGetController(string Name, out IController Controller)
        {
            using (LockingContext.ReadLock)
                return BaseControllers.TryGetValue(Name, out Controller);
        }

        public IController ResolveController(IEnumerable<string> Path)
        {
            using (LockingContext.ReadLock)
            {
                IEnumerator<string> Enumerator = Path.GetEnumerator();

                if (Enumerator.MoveNext() == false)
                    throw new ControllerPathResolutionException(Path, "it is empty");

                IController Current;

                if (TryGetController(Enumerator.Current, out Current) == false)
                    throw new ControllerPathResolutionException(Path,
                        "could not get the base controller " + Enumerator.Current);

                while (Enumerator.MoveNext())
                {
                    PropertyInfo Property;

                    try
                    {
                        Property = Current.GetType().GetProperty(Enumerator.Current);
                    }
                    catch (Exception)
                    {
                        throw new ControllerPathResolutionException(Path,
                            string.Format("the property {0} could not be reflected off of an object of type {1}",
                            Enumerator.Current, Current.GetType().Name));
                    }

                    var Value = Property.GetValue(Current, null);

                    if (Value == null)
                        throw new ControllerPathResolutionException(Path,
                            string.Format("the property {0} is null", Enumerator.Current));

                    Current = Value as IController;

                    if (Current == null)
                        throw new ControllerPathResolutionException(Path,
                            string.Format("the type {0} does not implement IController",
                            Value.GetType().Name));
                }

                return Current;
            }
        }

        private IDictionary<string, IController> BaseControllers;
        private LockingContext LockingContext;
    }
}
